package com.smart.mac.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.smart.mac.common.CommonConstants;
import com.smart.mac.config.FlowServiceConfig;
import com.smart.mac.model.dto.FlowQueryDTO;
import com.smart.mac.model.dto.FlowTaskDTO;
import com.smart.mac.model.vo.*;
import com.smart.mac.service.FlowTaskService;
import com.smart.mac.utls.FlowUtils;
import com.smart.mac.utls.LocalSessionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.impl.cmd.DeleteMultiInstanceExecutionCmd;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * FlowTaskServiceImpl
 *
 * @Author mac
 */
@Slf4j
@Service
public class FlowTaskServiceImpl extends FlowServiceConfig implements FlowTaskService {

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void backTask(String instanceId, String taskDefKey) {
        //获取当前任务
        List<HistoricTaskInstance> tenantHistoricTaskInstanceList = getTenantHistoricTaskInstanceList(instanceId);
        Assert.isTrue(CollectionUtil.isNotEmpty(tenantHistoricTaskInstanceList),"历史任务不存在，不能撤回");
        int size = tenantHistoricTaskInstanceList.size();
        for (int i = 0;i < size;i++){
            HistoricTaskInstance taskInstance = tenantHistoricTaskInstanceList.get(i);
            if (taskInstance.getTaskDefinitionKey().equals(taskDefKey) && (size -1-i) > 1){
                 throw new RuntimeException("任务已审核，不能撤回");
            }
        }
        List<Task> tenantTaskList = getTenantTaskList(instanceId);
        Assert.isTrue(CollectionUtil.isNotEmpty(tenantTaskList),"流程实例未开始或已结束，不能撤回");
        Assert.isTrue(tenantTaskList.stream().anyMatch(x -> Objects.nonNull(x.getAssignee())),"任务已签收，不能撤回");
        List<String> collect = tenantTaskList.stream().map(Task::getId).collect(Collectors.toList());
        // 设置回退意见
        tenantTaskList.forEach(currentTaskId -> taskService.addComment(currentTaskId.getId(), instanceId,null, "撤回"));
        //撤回
        runtimeService.createChangeActivityStateBuilder().processInstanceId(instanceId).moveActivityIdsToSingleActivityId(collect,taskDefKey).changeState();
    }

    @Override
    public Page<FlowProcInstVO> myProcess(FlowQueryDTO queryDTO) {
        //租户ID，TODO: 正式开发从SecurityContext获取就行
        UserDemoVO userDemo = (UserDemoVO) LocalSessionUtils.get();
        String tenantId = userDemo.getCompanyId();
        String userId = userDemo.getId();
        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
                .processInstanceTenantId(tenantId)
                .startedBy(userId);
        if (StringUtils.isNotEmpty(queryDTO.getName())) {
            historicProcessInstanceQuery.processInstanceNameLikeIgnoreCase(queryDTO.getName());
        }
        historicProcessInstanceQuery.orderByProcessInstanceStartTime()
                .desc();
        List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery.listPage(queryDTO.getPageSize() * (queryDTO.getPageNum() - 1), queryDTO.getPageSize());
        Page<FlowProcInstVO> page = Page.of(queryDTO.getPageNum(),queryDTO.getPageSize());
        page.setTotal(historicProcessInstanceQuery.count());
        List<FlowProcInstVO> collect = historicProcessInstances.stream().map(x -> {
            FlowProcInstVO instVO = new FlowProcInstVO();
            BeanUtil.copyProperties(x, instVO);
            instVO.setFinishTime(x.getEndTime());
            if (x.getDurationInMillis() != null) {
                instVO.setDuration(x.getDurationInMillis() / 1000 / 60);
            }
            instVO.setProcDefId(x.getProcessDefinitionId());
            instVO.setProcInsId(x.getSuperProcessInstanceId());
            instVO.setProcDefKey(x.getProcessDefinitionKey());
            instVO.setProcDefName(x.getProcessDefinitionName());
            return instVO;
        }).collect(Collectors.toList());
        page.setRecords(collect);
        return page;
    }

    @Override
    public Page<FlowTaskVO> todoList(FlowQueryDTO queryDTO) {
        Page<FlowTaskVO> page = Page.of(queryDTO.getPageNum(),queryDTO.getPageSize());
        //租户ID，TODO: 正式开发从SecurityContext获取就行
        UserDemoVO userDemo = (UserDemoVO) LocalSessionUtils.get();
        String tenantId = userDemo.getCompanyId();
        String userId = userDemo.getId();
        TaskQuery taskQuery = taskService.createTaskQuery()
                .active()
                .includeProcessVariables()
                .taskTenantId(tenantId)
                //.taskCandidateGroupIn("")
                .taskCandidateOrAssigned(userId)
                .orderByTaskCreateTime().desc();
        if (StringUtils.isNotEmpty(queryDTO.getName())){
            taskQuery.taskNameLike(queryDTO.getName());
        }
        page.setTotal(taskQuery.count());
        List<Task> taskList = taskQuery.listPage(queryDTO.getPageSize() * (queryDTO.getPageNum() - 1), queryDTO.getPageSize());
        List<FlowTaskVO> collect = taskList.stream().map(task -> {
            FlowTaskVO flowTask = new FlowTaskVO();
            flowTask.setTaskId(task.getId());
            flowTask.setTaskDefKey(task.getTaskDefinitionKey());
            flowTask.setCreateTime(task.getCreateTime());
            flowTask.setProcDefId(task.getProcessDefinitionId());
            flowTask.setTaskName(task.getName());
            // 流程定义信息
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(task.getProcessDefinitionId())
                    .singleResult();
            flowTask.setProcDefName(pd.getName());
            flowTask.setProcDefVersion(pd.getVersion());
            flowTask.setProcInsId(task.getProcessInstanceId());
            flowTask.setProcDefKey(pd.getKey());
            // 流程发起人信息
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(task.getProcessInstanceId())
                    .singleResult();
            flowTask.setStartUserId(historicProcessInstance.getStartUserId());
            return flowTask;
        }).collect(Collectors.toList());
        page.setRecords(collect);
        return page;
    }

    @Override
    public Page<FlowTaskVO> finishedList(FlowQueryDTO queryDTO) {
        Page<FlowTaskVO> page = Page.of(queryDTO.getPageNum(),queryDTO.getPageSize());
        //租户ID，TODO: 正式开发从SecurityContext获取就行
        UserDemoVO userDemo = (UserDemoVO) LocalSessionUtils.get();
        String tenantId = userDemo.getCompanyId();
        String userId = userDemo.getId();
        HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .includeProcessVariables()
                .finished()
                .taskTenantId(tenantId)
                .taskAssignee(userId)
                .orderByHistoricTaskInstanceEndTime()
                .desc();
        List<HistoricTaskInstance> historicTaskInstanceList = taskInstanceQuery.listPage(queryDTO.getPageSize() * (queryDTO.getPageNum() - 1), queryDTO.getPageSize());
        List<FlowTaskVO> collect = historicTaskInstanceList.stream().map(histTask -> {
            FlowTaskVO flowTask = new FlowTaskVO();
            // 当前流程信息
            flowTask.setTaskId(histTask.getId());
            // 审批人员信息
            flowTask.setCreateTime(histTask.getCreateTime());
            flowTask.setFinishTime(histTask.getEndTime());
            flowTask.setDuration(histTask.getDurationInMillis() / 1000 / 60);
            flowTask.setProcDefId(histTask.getProcessDefinitionId());
            flowTask.setTaskDefKey(histTask.getTaskDefinitionKey());
            flowTask.setTaskName(histTask.getName());

            // 流程定义信息
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(histTask.getProcessDefinitionId())
                    .singleResult();
            flowTask.setProcDefName(pd.getName());
            flowTask.setProcDefVersion(pd.getVersion());
            flowTask.setProcInsId(histTask.getProcessInstanceId());
            flowTask.setProcDefKey(pd.getKey());
            // 流程发起人信息
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(histTask.getProcessInstanceId())
                    .singleResult();
            flowTask.setStartUserId(historicProcessInstance.getStartUserId());
            return flowTask;
        }).collect(Collectors.toList());
        page.setTotal(taskInstanceQuery.count());
        page.setRecords(collect);
        return page;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean complete(FlowTaskDTO taskDTO) {
        Task task = getTenantTask(taskDTO.getTaskId());
        //委派
        if (DelegationState.PENDING.equals(task.getDelegationState())){
            taskService.addComment(taskDTO.getTaskId(),task.getProcessInstanceId(),taskDTO.getComment());
            taskService.resolveTask(taskDTO.getTaskId(),taskDTO.getVariables());
        }else {
            taskService.addComment(taskDTO.getTaskId(),task.getProcessInstanceId(),taskDTO.getComment());
            taskService.complete(taskDTO.getTaskId(),taskDTO.getVariables());
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delegateTask(FlowTaskDTO taskDTO) {
        taskService.delegateTask(taskDTO.getTaskId(),taskDTO.getAssignee());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void assignTask(FlowTaskDTO taskDTO) {
        taskService.setAssignee(taskDTO.getTaskId(),taskDTO.getAssignee());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void claim(FlowTaskDTO taskDTO) {
        //租户ID，TODO: 正式开发从SecurityContext获取就行
        UserDemoVO userDemo = (UserDemoVO) LocalSessionUtils.get();
        String userId = userDemo.getId();
        taskService.claim(taskDTO.getTaskId(),userId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unClaim(FlowTaskDTO taskDTO) {
        taskService.unclaim(taskDTO.getTaskId());
    }

    /**
    * act_ru_task,act_ru_identitylink各增加一条数据
    */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addSign(FlowTaskDTO taskDTO) {
       // managementService.executeCommand(new AddMultiInstanceExecutionCmd(taskDTO.getNodeDefKey(),taskDTO.getInstanceId(),taskDTO.getVariables()));
        Task task = getTenantTask(taskDTO.getTaskId());
        if (CommonConstants.BEFORE_ADDSIGN.equals(task.getScopeType()) && (taskDTO.getBeforeAddSign() == null || taskDTO.getBeforeAddSign())){
            throw new RuntimeException("已经加签，请先完成再加后置签");
        }else if (CommonConstants.AFTER_ADDSIGN.equals(task.getScopeType()) && (taskDTO.getBeforeAddSign() != null && !taskDTO.getBeforeAddSign())){
            throw new RuntimeException("后置签已添加，不可重复添加");
        }
        TaskEntity taskEntity = (TaskEntity) task;
        if (taskDTO.getBeforeAddSign() == null || taskDTO.getBeforeAddSign()){
            taskEntity.setOwner(task.getAssignee());
            taskEntity.setAssignee(taskDTO.getAssignee());
            taskEntity.setScopeType(CommonConstants.BEFORE_ADDSIGN);
            taskEntity.setDelegationState(DelegationState.PENDING);
            taskService.saveTask(taskEntity);
        }else{
            //租户ID，TODO: 正式开发从SecurityContext获取就行
            UserDemoVO userDemo = (UserDemoVO)LocalSessionUtils.get();
            taskEntity.setAssignee(userDemo.getId());
            taskEntity.setOwner(taskDTO.getAssignee());
            taskEntity.setDelegationState(DelegationState.PENDING);
            taskEntity.setScopeType(CommonConstants.AFTER_ADDSIGN);
            taskService.saveTask(taskEntity);
        }

    }

    /**
     * 创建子任务
     *
     * @param ptask    创建子任务
     * @param assignee 子任务的执行人
     * @return
     */
    protected TaskEntity createSubTask(TaskEntity ptask, Boolean beforeAddSign, String assignee) {
        TaskEntity task = null;
        if (ptask != null) {
            //1.生成子任务
            task = (TaskEntity) taskService.newTask();
            task.setCategory(ptask.getCategory());
            task.setDescription(ptask.getDescription());
            task.setTenantId(ptask.getTenantId());
            task.setAssignee(assignee);
            task.setName(ptask.getName());
            task.setScopeType(beforeAddSign == null || beforeAddSign?CommonConstants.BEFORE_ADDSIGN:CommonConstants.AFTER_ADDSIGN);
            task.setParentTaskId(ptask.getParentTaskId());
            task.setProcessDefinitionId(ptask.getProcessDefinitionId());
            task.setProcessInstanceId(ptask.getProcessInstanceId());
            task.setTaskDefinitionKey(ptask.getTaskDefinitionKey());
            task.setTaskDefinitionId(ptask.getTaskDefinitionId());
            task.setPriority(ptask.getPriority());
            task.setCreateTime(new Date());
            taskService.saveTask(task);
        }
        return task;
    }


    /**
     * act_ru_task 减少一条,act_ru_identitylink不变
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteSign(FlowTaskDTO taskDTO) {
        managementService.executeCommand(new DeleteMultiInstanceExecutionCmd(taskDTO.getCurrentChildExecutionId(),taskDTO.getCurrentChildExecutionFlag()));
    }

    @Override
    public InputStream diagram(String procInstId) {
        HistoricProcessInstance processInstance = getTenantHistoricProcessInstance(procInstId);
        //查询流程活动节点
        List<HistoricActivityInstance> activityInstances = getTenantHistoricActivityInstance(procInstId);
        //高亮显示线
        List<String> highLightedFlows = new ArrayList<>();
        //高亮显示节点
        List<String> highLightedNodes = new ArrayList<>();
        for(HistoricActivityInstance ai : activityInstances){
            if (CommonConstants.SEQUENCEFLOW.equals(ai.getActivityType())){
                highLightedFlows.add(ai.getActivityId());
            }else{
                highLightedNodes.add(ai.getActivityId());
            }
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration();
        DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
        return diagramGenerator.generateDiagram(bpmnModel,
                "png",
                highLightedNodes,
                highLightedFlows,
                configuration.getActivityFontName(),
                configuration.getLabelFontName(),
                configuration.getAnnotationFontName(),
                configuration.getClassLoader(),
                1.0,
                true);
    }


    @Override
    public void taskReject(FlowTaskDTO taskDTO) {
        //获取当前任务
        Task task = getTenantTask(taskDTO.getTaskId());
        if (task.isSuspended()){
            throw new RuntimeException("任务处于挂起状态");
        }
        //获取流程模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        //获取流程
        Process process = bpmnModel.getProcesses().get(0);
        Collection<FlowElement> allFlowElements = FlowUtils.getAllFlowElements(process.getFlowElements(), null);
        Assert.isTrue(CollectionUtil.isNotEmpty(allFlowElements),"当前流程没有节点");
        FlowElement source = null;
        FlowElement target = null;
        for (FlowElement flowElement : allFlowElements) {
            // 当前任务节点元素
            if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                source = flowElement;
            }
            // 跳转的节点元素
            if (flowElement.getId().equals(taskDTO.getTargetKey())) {
                target = flowElement;
            }
        }
        Assert.notNull(target,"驳回目标任务key不存在");
        //校验目标系欸但是否是同一路线上，或网关上等
        Boolean checked = FlowUtils.eachCheckRefTarget(source,taskDTO.getTargetKey(),null,null);
        if (!checked){
            throw new RuntimeException("当前节点相对于目标节点，不属于串行关系，不能驳回");
        }
        List<Task> runTaskList = getTenantTaskList(task.getProcessInstanceId());
        List<String> runTaskKeyList = runTaskList.stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
        //遍历查找目标之后的任务节点
        List<UserTask> currentUserTaskList = FlowUtils.eachFindChildUserTasks(target, runTaskKeyList, null, null);
        List<String> currentIds = currentUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList());
        // 循环获取那些需要被撤回的节点的ID，用来设置驳回原因
        List<String> currentTaskIds = new ArrayList<>();
        currentIds.forEach(cur -> runTaskList.forEach(run -> {
            if (cur.equals(run.getTaskDefinitionKey())) {
                currentTaskIds.add(run.getId());
            }
        }));
        // 设置回退意见
        currentTaskIds.forEach(currentTaskId -> taskService.addComment(currentTaskId, task.getProcessInstanceId(), taskDTO.getComment()));
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(task.getProcessInstanceId())
                .moveActivityIdsToSingleActivityId(currentIds, taskDTO.getTargetKey()).changeState();

    }

    @Override
    public Map<String, Object> flowRecord(String procInsId) {
        Assert.isTrue(StringUtils.isNotEmpty(procInsId),"流程实例ID不能为空");
        Map<String, Object> map = new HashMap<>();
        List<HistoricActivityInstance> tenantHistoriActivityList = getTenantHistoriActivityList(procInsId);
        if (CollectionUtil.isNotEmpty(tenantHistoriActivityList)){
            List<FlowTaskVO> flowRecords = tenantHistoriActivityList.stream().filter(x -> StringUtils.isNotEmpty(x.getTaskId())).map(histIns -> {
                FlowTaskVO flowTask = new FlowTaskVO();
                flowTask.setTaskId(histIns.getTaskId());
                flowTask.setTaskName(histIns.getActivityName());
                flowTask.setCreateTime(histIns.getStartTime());
                flowTask.setFinishTime(histIns.getEndTime());
                if (histIns.getDurationInMillis() != null) {
                    flowTask.setDuration(histIns.getDurationInMillis() / 1000 / 60);
                }
                flowTask.setAssigneeId(histIns.getAssignee());
                flowTask.setProcInsId(histIns.getProcessInstanceId());
                //获取人员
                List<HistoricIdentityLink> historicIdentityLinksForTask = historyService.getHistoricIdentityLinksForTask(histIns.getTaskId());
                if (CollectionUtil.isNotEmpty(historicIdentityLinksForTask)) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (HistoricIdentityLink identityLink : historicIdentityLinksForTask) {
                        //候选组
                        if (CommonConstants.CANDIDATE.equals(identityLink.getType())) {
                            if (StringUtils.isNotEmpty(identityLink.getUserId())) {
                                stringBuilder.append(identityLink.getUserId()).append(",");
                            }
                            if (StringUtils.isNotEmpty(identityLink.getGroupId())) {
                                stringBuilder.append(identityLink.getGroupId()).append(",");
                            }
                        }
                    }
                    if (stringBuilder.length() > 0) {
                        flowTask.setCandidate(stringBuilder.toString().substring(0, stringBuilder.length() - 1));
                    }
                }
                //获取评论
                List<Comment> taskComments = taskService.getTaskComments(histIns.getTaskId());
                if (CollectionUtil.isNotEmpty(taskComments)) {
                    Comment comment = taskComments.get(0);
                    flowTask.setComment(FlowCommentVO.builder().type(comment.getType()).comment(comment.getFullMessage()).build());
                }
                return flowTask;
            }).collect(Collectors.toList());
            map.put("flowRecords",flowRecords);
            //查询表单
            List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery()
                    .processInstanceId(procInsId).variableName(CommonConstants.FORMDATA_KEY).listPage(0, 1);
            if (CollectionUtil.isNotEmpty(historicVariableInstances)){
                map.put(CommonConstants.FORMDATA_KEY,historicVariableInstances.get(0).getValue());
            }
        }
        return map;
    }

    @Override
    public List<UserTaskSimpleVO> findReturnTaskList(FlowTaskDTO taskDTO) {
        Task task = getTenantTask(taskDTO.getTaskId());
        // 获取所有节点信息
        Process process = repositoryService.getBpmnModel(task.getProcessDefinitionId()).getProcesses().get(0);
        Collection<FlowElement> flowElements = process.getFlowElements();
        Assert.isTrue(CollectionUtil.isNotEmpty(flowElements),"流程图没有节点");
        FlowElement source = null;
        for (FlowElement element : flowElements){
           if (task.getTaskDefinitionKey().equals(element.getId())){
               source = element;
               break;
           }
        }
        // 获取节点的所有路线
        List<UserTask> roads = FlowUtils.findRoad(source,null, null);
        Assert.isTrue(CollectionUtil.isNotEmpty(roads),"流程图没有任务节点");
        return roads.stream().map(x ->{
            UserTaskSimpleVO userTaskSimpleVO = new UserTaskSimpleVO();
            userTaskSimpleVO.setName(x.getName());
            userTaskSimpleVO.setTaskDefKey(x.getId());
            userTaskSimpleVO.setAssignee(x.getAssignee());
            userTaskSimpleVO.setOwner(x.getOwner());
            return userTaskSimpleVO;
        }).collect(Collectors.toList());
    }
}
